
#include <fstream>
#include <iostream>

#include "Tribool.hpp"
#include "Symbols.hpp"
#include "ExpressionParser.hpp"
#include "OperatorNode.hpp"
#include "ValueNode.hpp"
#include "OutAndNode.hpp"
#include "OutOrNode.hpp"

ExpressionParser::ExpressionParser()
{
    this->_operatorPriority[NOT] = 1;
    this->_operatorPriority[AND] = 2;
    this->_operatorPriority[OR] = 3;
    this->_operatorPriority[XOR] = 3;
}

ExpressionParser::~ExpressionParser()
{

}

bool ExpressionParser::getDataFromFile(const std::string &filename, Universe& universe)
{
    std::ifstream ifs(filename.c_str());
    if (ifs.is_open())
    {
        char buffer[64];
        while (ifs.getline(buffer, 64))
        {
            if (buffer[0] != 0 && buffer[0] != ';')
            {
                std::pair<char, Tribool> fact = this->peekFact(buffer);
                if (fact.first != -1)
                    universe.addFact(fact.first, fact.second);
                else
                {
                    if (!this->peekRule(buffer, universe))
                    {
                        ifs.close();
                        return false;
                    }
                }
            }
        }
        ifs.close();
        return true;
    }
    else
        std::cout << "cannot open file " << filename << std::endl;
    return false;
}

bool ExpressionParser::getResultFromFile(std::string const & filename, Universe& universe)
{
    std::ifstream ifs(filename.c_str());
    if (ifs.is_open())
    {
        char buffer[64];
        while (ifs.getline(buffer, 64))
        {
            if (buffer[0] != 0 && buffer[0] != ';')
            {
                if (!this->peekResult(buffer, universe))
                {
                    ifs.close();
                    return false;
                }
            }
        }
        ifs.close();
        return true;
    }
    else
        std::cout << "cannot open file " << filename << std::endl;
    return false;
}

bool ExpressionParser::peekResult(char const * stream, Universe& universe)
{
    while (*stream != 0)
    {
        if (this->_isVariable(*stream))
            universe.addResult(*stream);
        else if (!this->_isBlank(*stream))
            return false;
        stream++;
    }
    return true;
}

std::pair<char, Tribool> ExpressionParser::peekFact(char const *stream)
{
    Tribool tb(TriboolVal::Undefined);
    stream = this->_ignoreBlanks(stream);
    if (*stream == IMPLY)
    {
        stream = this->_ignoreBlanks(stream + 1);
        if (*stream == NOT)
        {
            tb.value = TriboolVal::False;
            stream = this->_ignoreBlanks(stream + 1);
        }
        else
            tb.value = TriboolVal::True;
        if (this->_isVariable(*stream))
        {
            return std::pair<char, Tribool>(*stream, tb);
        }
    }
    return std::pair<char, Tribool>(-1, tb);
}

bool ExpressionParser::peekRule(char const * stream, Universe& universe)
{
    std::string s_rule(stream);
    unsigned int i_imply = s_rule.find(IMPLY);
    if (i_imply + 1 < s_rule.size())
    {
        std::string s_antecedents = s_rule.substr(0, i_imply);
        std::string s_conclusion = s_rule.substr(i_imply + 1);
        ExpressionNode* antecedents = this->_parseAntecedents(s_antecedents, universe);
        if (antecedents != 0)
        {
            ExpressionNode* conclusion = this->_parseConclusion(s_conclusion, universe);
            if (conclusion != 0)
            {
                conclusion->addChild(antecedents);
                return true;
            }
        }
    }
    std::cerr << "error, unavailable expression" << std::endl;
    return false;
}

ExpressionNode* ExpressionParser::_parseAntecedents(std::string const & expr, Universe& universe)
{
    int i = this->_findOperatorMinPriority(expr);
    if (i != -1 && i + 1 < static_cast<int>(expr.size()))
    {
        std::string s_left = expr.substr(0, i);
        std::string s_right = expr.substr(i + 1);
        ExpressionNode* left = (expr[i] == NOT) ? 0 : this->_parseAntecedents(s_left, universe);
        ExpressionNode* right = this->_parseAntecedents(s_right, universe);
        if ((left != 0 || expr[i] == NOT) && right != 0)
        {
            OperatorNode* node = new OperatorNode(expr[i]);
            node->setLeft(left);
            node->setRight(right);
            return node;
        }
    }
    char value = this->_getValue(expr);
    if (value != -1)
        return universe.addValueNode(value);
    return 0;
}

ExpressionNode* ExpressionParser::_parseConclusion(std::string const & expr, Universe& universe)
{
    int i = this->_findOperatorMinPriority(expr);
    if (i != -1 && i + 1 < static_cast<int>(expr.size()))
    {
        std::string s_left = expr.substr(0, i);
        std::string s_right = expr.substr(i + 1);
        ExpressionNode* left = (expr[i] == NOT) ? 0 : this->_parseConclusion(s_left, universe);
        ExpressionNode* right = this->_parseConclusion(s_right, universe);
        if ((left != 0 || expr[i] == NOT) && right != 0)
        {
            switch (expr[i])
            {
            case AND:
                {
                    OutAndNode* node = new OutAndNode();
                    left->addChild(node);
                    right->addChild(node);
                    return node;
                }
                break;
            case NOT:
                {
                    OperatorNode* node = new OperatorNode(NOT);
                    right->addChild(node);
                    return node;
                }
                break;
            case OR:
                {
                    OutOrNode* node = new OutOrNode();
                    left->addChild(node);
                    right->addChild(node);
                    node->setLeftParent(left);
                    node->setRightParent(right);
                    return node;
                }
                break;
            case XOR:
                {
                    OutOrNode* node = new OutOrNode(true);
                    left->addChild(node);
                    right->addChild(node);
                    node->setLeftParent(left);
                    node->setRightParent(right);
                    return node;
                }
                break;
            }
        }
    }
    char value = this->_getValue(expr);
    if (value != -1)
        return universe.addValueNode(value);
    return 0;
}

int ExpressionParser::_findOperatorMinPriority(std::string const & str)
{
    int i = 0;
    int r = -1;
    int size = str.size();
    while (i < size)
    {
        if (this->_isOperator(str[i]) && (r == -1 || this->_operatorPriority[str[i]] > this->_operatorPriority[str[r]]))
            r = i;
        i++;
    }
    return r;
}

char ExpressionParser::_getValue(std::string const & expr)
{
    char value = -1;
    for (unsigned int i = 0; i < expr.size(); i++)
    {
        if (value == -1)
        {
            if (!this->_isBlank(expr[i]))
            {
                if (this->_isVariable(expr[i]))
                    value = expr[i];
                else
                    return -1;
            }
        } else {
            if (!this->_isBlank(expr[i]))
                return -1;
        }
    }
    return value;
}

char const * ExpressionParser::_ignoreBlanks(char const * stream) const
{
    while (*stream != 0 && this->_isBlank(*stream))
        stream++;
    return stream;
}

bool ExpressionParser::_isBlank(char c) const
{
    int i = 0;
    while (BLANKS[i] != '\0')
    {
        if (BLANKS[i] == c)
            return true;
        i++;
    }
    return false;
}

bool ExpressionParser::_isVariable(char c) const
{
    int i = 0;
    while (VARIABLES[i] != '\0')
    {
        if (VARIABLES[i] == c)
            return true;
        i++;
    }
    return false;
}

bool ExpressionParser::_isOperator(char c) const
{
    if (c == AND || c == OR || c == XOR || c == NOT)
        return true;
    return false;
}
